package net.teamclerks.rpgf.script.action;

import net.teamclerks.rpgf.avatar.Avatar;
import net.teamclerks.rpgf.script.director.Director;

/**
 * The Action class is the basis for interaction between Avatars.<br/>
 * RPGF constructs all interactable-objects as Avatars
 * so that any interactable object can also interact or act.<br/>
 * <br/>
 * The idea is that when a Map is loaded, so too are the Avatars for that map.
 * Each Avatar on that map is unique (sometimes even to themselves given a point in "game time")
 * and thus a list of Actions are stored with each Avatar.
 * <br/>
 * <br/>Example:<br/>
 * A treasure chest can be acted upon by being opened.<br/>
 * A treasure chest can act by putting something into an inventory.<br/>
 * Here, the player has Action(PlayerAvatar,ChestAvatar), which is a ChestAction,
 * which tells the Director to place a particular item into a player's inventory.<br/><br/>
 * A player-character can act by talking to an NPC.<br/>
 * An NPC can act by responding to a player-character's request for action.<br/>
 * Here, the player has DialogAction(PlayerAvatar,NPCAvatar) which tells the 
 * Director to open a dialog given the ID of the first DialogAction encountered
 * in the NPC's list of actions.
 * 
 * @author msmith
 *
 */
public abstract class Action
{  
  /// CONTROL DATA ///
  /**
   * The Avatar who is taking this action.
   */
  protected Avatar actor;
  /**
   * The Avatar who is receiving this action.
   */
  protected Avatar recipient;
  /**
   * Whether calling act() against this Action will destroy this object.
   */
  protected boolean destructive;
  /**
   * If calling <code>act</code> on this Action should fire some other action,
   * then it is considered "chainedTo" the other Action. If chainedTo is null,
   * then it is considered to be an atomic action which should just act normally.<br/><br/>
   * If this Action has a chainedTo Action and this Action is destructive, the
   * default implementation of the <code>act</code> method should take the
   * following course:<br/>
   * &nbsp;&nbsp;&nbsp;1) Complete the action (chests put items into inventories, etc).<br/>
   * &nbsp;&nbsp;&nbsp;2) Set the <code>recipient</code>'s Action to the <code>chainedTo</code> of this Action.<br/>
   * &nbsp;&nbsp;&nbsp;3) Destroy this Action.<br/>
   * &nbsp;&nbsp;&nbsp;4) Call the recipient's Action. 
   */
  protected Action chainedTo;
  
  /**
   * The Director to call back to when an action takes place.
   */
  protected Director director;
  
  /// DENY CONSTRUCTOR ///
  private Action()
  {
    actor = null;
    recipient = null;
    destructive = false;
    chainedTo = null;
  }
  
  /// PUBLIC CONSTRUCTOR ///
  public Action(Director director)
  {
    this();
    this.director = director;
  }
  
  /// PUBLIC METHODS ///
  /**
   * doAction is the public way to tell an Action to act.
   * It also handles the cleanup of the Action if it is
   * destructive or chainedTo another Action.
   */
  public final void doAction()
  {
    act();
    
    if(this.getChainedTo() != null)
    {
      // ChainedTo should know who its Actor and Recipient are already.
      Action chainedTo = this.getChainedTo();
      
      // Fire the chainedTo Action if present.
      if(chainedTo != null)
      {
        chainedTo.act();
      }
    }
    
    if(this.isDestructive())
    {
      this.director.getScene().getScript().removeAction(this);
    }
  }

  /**
   * Gets the actor which is preforming this Action.
   */
  public final Avatar getActor()
  {
    return this.actor;
  }
  /**
   * Sets the actor who is preforming this Action.
   * @param actor
   */
  public final void setActor(Avatar actor)
  {
    this.actor = actor;
  }
  /**
   * Gets the recipient of this Action.
   * @return recipient
   */
  public final Avatar getRecipient()
  {
    return this.recipient;
  }
  /**
   * Sets the recipient of this Action.
   * @param recipient
   */
  public final void setRecipient(Avatar recipient)
  {
    this.recipient = recipient;
  }
  /**
   * Returns whether this Action is destructive (determines 
   * whether calling act() on this Action will destroy this
   * Action).
   * @return
   */
  public final boolean isDestructive()
  {
    return this.destructive;
  }
  /**
   * Sets whether this Action is destructive.
   * @param destructive
   */
  public final void setDestructive(boolean destructive)
  {
    this.destructive = destructive;
  }
  /**
   * Returns the Action that this Action is chained to (null implies
   * this Action chains to no other Action).
   * @return chainedTo
   */
  public final Action getChainedTo()
  {
    return chainedTo;
  }
  /**
   * Sets the Action which this Action is chained to (null implies
   * this Action chains to no other Action).
   * @param chainedTo
   */
  public final void setChainedTo(Action chainedTo)
  {
    this.chainedTo = chainedTo;
  }
  
  /// PROTECTED METHODS ///
  /**
   * The meat-n-potatoes of Action. The act method tells this implementation
   * to do whatever it is that this Action does.<br/>
   * <br/>
   * All subclasses should override this method, and all subclasses should call
   * <code>this.getChainedTo().act()</code> after completing their Action to
   * fire off the chained Actions.
   */
  protected abstract void act();
}
